#include "StdAfx.h"
//-------------------------------------------------------------------------------------------------
//ImageProcStep.cpp  - Image Processing Step Implementation       
//-------------------------------------------------------------------------------------------------
//FreeBSD License
//Copyright 2012 Will Stott All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are
//permitted provided that the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice, this list of
//      conditions and the following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above copyright notice, this list
//      of conditions and the following disclaimer in the documentation and/or other materials
//      provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY Will Stott ``AS IS'' AND ANY EXPRESS OR IMPLIED
//WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Will Stott OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//The views and conclusions contained in the software and documentation are those of the
//authors and should not be interpreted as representing official policies, either expressed
//or implied, of Will Stott.
//
//----Authors--------------------------------------------------------------------------------------
//created by: will.stott@ucl.ac.uk Feb 2012
//
//----Comments-------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------

#include "tinyxml.h"
#include "TinyXmlHelper.h"
#include "ImageExptLib.h"
#include "ImageProcStep.h"

const char * const ImageProcStep::xmlProcStep = "Step";
const char * const ImageProcStep::attribStepNum = "StepNum";
const char * const ImageProcStep::attribStepAnalysis = "AnalysisStep";
const char * const ImageProcStep::attribStepAnalysisYes = "yes";
const char * const ImageProcStep::attribLibName = "LibName";
const char * const ImageProcStep::attribLibVersion = "LibVer";
const char * const ImageProcStep::attribInterfaceVersion = "IVer";
const char * const ImageProcStep::attribFunctionName = "Function";

const char * const ImageProcStep::InvalidIntermedImageFile = "InvalidFile";
const char * const ImageProcStep::NoDescription = "NoDescription";
const char * const ImageProcStep::NoResult = "Error: no result found";
const char * const ImageProcStep::NoScore = "Error: no score found";

ImageProcStep::ImageProcStep(void) : 
	_error(true),
	_Description(ImageProcStep::NoDescription),
	_IntermediateImagePathFilename(ImageProcStep::InvalidIntermedImageFile),
	_StepNo(ImageProcStep::InvalidStepNum),
	_lib('\0'),
	_fn('\0'),
	_inputs('\0'),
	_outputs('\0')
{

}

ImageProcStep::ImageProcStep(ImageProcStep *step) :
	_error(true),
	_Description(ImageProcStep::NoDescription),
	_StepNo(ImageProcStep::InvalidStepNum),
	_lib('\0'),
	_fn('\0'),
	_inputs('\0'),
	_outputs('\0')
{
	if (step != '\0')
	{
		_error = step->_error;
		_Description = step->_Description;
		_StepNo = step->_StepNo;
		_lib = step->_lib;
		_fn = step->_fn;
		_inputs = new ProcParamsInputs(step->_inputs);
		_outputs = new ProcParamsOutputs(step->_outputs);
	}
}

ImageProcStep::ImageProcStep(int SeqNo) :		//protected, only used by UnitTestValidStep
	_error(true),
	_Description(ImageProcStep::NoDescription),
	_StepNo(ImageProcStep::InvalidStepNum),
	_lib('\0'),
	_fn('\0'),
	_inputs('\0'),
	_outputs('\0')
{
	_StepNo = SeqNo;
}

ImageProcStep::ImageProcStep(int StepNo, IImageProcStepLibDyn *pLib, IImageProcFn *pFn, ProcParamsInputs *pInputs, ProcParamsOutputs *pOutputs) :
	_error(true),							//this makes a copy of inputs and outputs, but lib and fn are shared amongst all ImpageProcSteps as they are readonly  
	_Description(ImageProcStep::NoDescription),
	_StepNo(StepNo),
	_lib(pLib),
	_fn('\0'),
	_inputs(pInputs),
	_outputs(pOutputs)
{
	if ((pFn != '\0') && (pFn->IsApiValid() == true))
	{
		_fn = pFn->GetApi();

		if ((_inputs = new ProcParamsInputs(pInputs)) == '\0')
			ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: step %d, failed to create _inputs",  _StepNo);
		else
		{
			if ((_outputs = new ProcParamsOutputs(pOutputs)) == '\0')
			{
				ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: step %d, failed to create _outputs",  _StepNo);
				delete _inputs;
				_inputs = '\0';
			}
		}
		_error = (Validate() == true)  ? false : true;
		if (_error == true)
			ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: failed to validate processStep %d: %s", _StepNo, GetString());
	}
}

ImageProcStep::ImageProcStep(TiXmlElement *processStep, ImageProcStepLibs *procLibs) : 
	_error(true),
	_StepNo(ImageProcStep::InvalidStepNum),
	_lib('\0'),
	_fn('\0'),
	_inputs('\0'),
	_outputs('\0')
{
	if ( (processStep == '\0') || (procLibs == '\0'))
		ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: processStep is %s, procLibs is %s", ((processStep == '\0') ? "null" : "not null"),  ((procLibs == '\0') ? "null" : "not null"));
	else
	{
		std::string ProcStepName(ImageProcStep::xmlProcStep);
		if ( ProcStepName != processStep->Value())
			ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: processStep <%s> is invalid", processStep->Value());
		else
		{
			const char *StepDesc = processStep->GetText();
			if (StepDesc == '\0')
				StepDesc = ImageProcStep::NoDescription;

			if (processStep->Attribute(ImageProcStep::attribStepNum, &_StepNo) == '\0')
				ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: <%s %s=???> attribute not found (%s)", ImageProcStep::xmlProcStep, ImageProcStep::attribStepNum, StepDesc);
			else
			{
				if (LoadStep(processStep, procLibs) == false)
					ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: failed to load processStep %d: %s", _StepNo, StepDesc);
				else
				{
					_error = (Validate() == true)  ? false : true;
					if (_error == true)
						ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "ERROR: failed to validate processStep %d: %s", _StepNo, StepDesc);
					else
						ImageExptLib::LogMsg("ImageProcStep::ImageProcStep", "SUCCESS: loaded and validated processStep %d: %s", _StepNo, StepDesc);
				}
			}
		}
	}
}

ImageProcStep::~ImageProcStep(void)
{
	//_lib and _fn belongs to ImageProcStepLibs *procLibs so do not delete here - it may be used by another step

	if (_inputs != '\0')	
		delete _inputs;
	_inputs = '\0';
	if (_outputs != '\0')	
		delete _outputs;
	_outputs = '\0';
}

bool ImageProcStep::Start(bool DebugMode)
{
	bool rc = false;

	if ((_lib == '\0') || (_lib->IsApiValid() == false) || (_fn == '\0'))
		ImageExptLib::LogMsg("ImageProcStep::Start", "ERROR: _lib is null or _lib->IsApiValid() == false or _fn is null");
	else
	{
		IImageProcFn *iFn = '\0';
		if ((iFn = _lib->GetLibApi()->GetFunction(_fn->GetFunctionName())) == '\0')
			ImageExptLib::LogMsg("ImageProcStep::Start", "ERROR: step %d, failed to find function %s in %s %s",  _StepNo, _fn->GetFunctionName(), _lib->GetLibName(), _lib->GetLibVersion());
		else
		{
			if (iFn->IsApiValid() == false)
				ImageExptLib::LogMsg("ImageProcStep::Start", "ERROR: step %d, function %s in %s %s is invalid",  _StepNo, _fn->GetFunctionName(), _lib->GetLibName(), _lib->GetLibVersion());
			else
				rc = iFn->Process(this, DebugMode);
		}
	}
	return rc;
}

bool ImageProcStep::Validate()
{
	bool rc = false;

	if ((_StepNo == ImageProcStep::InvalidStepNum) || (_lib == '\0') || (_fn == '\0') || (_inputs == '\0') || (_outputs == '\0'))
		ImageExptLib::LogMsg("ImageProcStep::Validate", "ERROR: step %d, not initialised - _lib is %s, _fn is %s, _inputs is %s, _outputs is %s", _StepNo, ((_lib == '\0') ? "null" : "no null"), ((_fn == '\0') ? "null" : "no null"), ((_inputs == '\0') ? "null" : "no null"), ((_outputs == '\0') ? "null" : "no null"));
	else
	{
		SetDescription();

		if (_lib->IsApiValid() == false)
			ImageExptLib::LogMsg("ImageProcStep::Validate", "ERROR: step %d, not initialised - _lib is not valid", _StepNo);
		else
		{
			if (_fn->Validate() == false)
				ImageExptLib::LogMsg("ImageProcStep::Validate", "ERROR: step %d, not initialised - _fn cannot be validated", _StepNo);
			else
			{
				if (_outputs->Validate() == false)
					ImageExptLib::LogMsg("ImageProcStep::Validate", "ERROR: step %d, not initialised - _outputs cannot be validated", _StepNo);
				else
				{
					if (_inputs->Validate() == false)
						ImageExptLib::LogMsg("ImageProcStep::Validate", "ERROR: step %d, not initialised - _inputs cannot be validated", _StepNo);
					else
					{
						if (_fn->IsMatch( _fn->GetFunctionName(), _outputs, _inputs) == false)
							ImageExptLib::LogMsg("ImageProcStep::Validate", "ERROR: step %d, parameters in Xml do not match definitions for function %s", _StepNo, _fn->GetFunctionName());
						else
							rc = true;
					}
				}
			}
		}
	}
	_error = ((rc == true) ? false : true);
		 
	return rc;
}

const char *ImageProcStep::GetScore()
{
	const char *rc = ImageProcStep::NoScore;

	if ( IsAnalysisStep() )
	{
		ProcParamsOutputs  *outputs = '\0';
		if ((outputs = GetOutputs()) == '\0')
			ImageExptLib::LogMsg("ImageProcStep::SaveResultsToXml()", "ERROR: outputs not initialised");
		else
		{
			ProcParam *param = '\0';
			if ( (param = outputs->GetParam(IPluginICProcStepsParamAttribOutputNameScore)) == '\0')
				ImageExptLib::LogMsg("ImageProcStep::SaveResultsToXml()", "ERROR: param %s not found", IPluginICProcStepsParamAttribOutputNameScore);
			else
			{
				rc = param->GetValueStr();
			}
		}
	}
	return rc;
}

const char *ImageProcStep::GetResult()
{
	const char *rc = ImageProcStep::NoResult;

	ProcParamsOutputs  *outputs = '\0';
	if ((outputs = GetOutputs()) == '\0')
		ImageExptLib::LogMsg("ImageProcStepAnalysis::SaveResultsToXml()", "ERROR: outputs not initialised");
	else
	{
		ProcParam *param = '\0';
		if ( (param = outputs->GetParam(IPluginICProcStepsParamAttribOutputNameResult)) == '\0')
			ImageExptLib::LogMsg("ImageProcStepAnalysis::SaveResultsToXml()", "ERROR: param %s not found", IPluginICProcStepsParamAttribOutputNameResult);
		else
		{
			rc = param->GetValueStr();
		}
	}
	return rc;
}

bool ImageProcStep::SaveResultsToXml(TiXmlElement *image)
{
	bool rc = false;

	if ( IsAnalysisStep() == false)
		rc = true;
	else
	{
		if (image == '\0')
			ImageExptLib::LogMsg("ImageProcStep::SaveResultsToXml()", "ERROR: image is null" );
		else
		{
			ProcParamsOutputs  *outputs = '\0';
			if ((outputs = GetOutputs()) == '\0')
				ImageExptLib::LogMsg("ImageProcStep::SaveResultsToXml()", "ERROR: outputs not initialised");
			else
			{
				ProcParam *param = '\0';
				if ( (param = outputs->GetParam(IPluginICProcStepsParamAttribOutputNameScore)) == '\0')
					ImageExptLib::LogMsg("ImageProcStep::SaveResultsToXml()", "ERROR: param %s not found", IPluginICProcStepsParamAttribOutputNameScore);
				else
				{
					image->SetAttribute(IPluginICProcStepsParamAttribOutputNameScore, param->GetValueStr());
					rc = true;
				}
			}
		}
	}
	return rc;
}

bool ImageProcStep::SaveSettingsToXml(TiXmlElement *xmlProcSteps)
{
	bool rc = false;

	if (xmlProcSteps == '\0')
		ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: body or lib is null");
	else
	{
		if ( IsValid() == false) 
			ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: ImageProcStep or lib is invalid");
		else
		{
			TiXmlElement *stepXml = TinyXmlLib::CreateSection (xmlProcSteps, ImageProcStep::xmlProcStep, _fn->GetFunctionDescription() );
			if (stepXml == '\0')
				ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: failed to create section for step");
			else
			{
				rc = true;
				stepXml->SetAttribute(ImageProcStep::attribStepNum, _StepNo);
				stepXml->SetAttribute(ImageProcStep::attribLibName, _lib->GetLibName());
				stepXml->SetAttribute(ImageProcStep::attribLibVersion, _lib->GetLibVersion());
				stepXml->SetAttribute(ImageProcStep::attribFunctionName, _fn->GetFunctionName());

				if (rc == true )
				{
					ProcParamsInputs  *inputs = '\0';
					if ((inputs = GetInputs()) == '\0')
					{
						ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: Inputs not initialised");
						rc = false;
					}
					else
					{
						TiXmlElement *inputsXml = TinyXmlLib::CreateSection (stepXml, ProcParamsInputs::xmlInputs, "function inputs" );
						if (inputsXml == '\0')
						{
							ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: failed to create section for inputsXml");
							rc = false;
						}
						else
						{
							int cnt = inputs->GetParamCount();
							for (int x = 0; x < cnt; x++ )
							{
								ProcParam *param = inputs->GetParam(x);
								if (param == '\0')
								{
									ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: %s failed to get input param %d", _fn->GetFunctionName(), x);
									rc = false;
								}
								else
								{
									TiXmlElement *paramXml = TinyXmlLib::CreateSection (inputsXml, IPluginICProcStepsParamXmlProcParam, "input param" );
									if (paramXml == '\0')
									{
										ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: failed to create section for in paramXml");
										rc = false;
									}
									else
									{
										paramXml->SetAttribute(IPluginICProcStepsParamAttribName, param->GetName());
										paramXml->SetAttribute(IPluginICProcStepsParamAttribIndex, param->GetIndex());
										paramXml->SetAttribute(IPluginICProcStepsParamAttribType, param->GetType());
										paramXml->SetAttribute(IPluginICProcStepsParamAttribValue, param->GetValueStr());
									}
								}
							}
						}
					}
				}
				if ( rc == true )
				{
					ProcParamsOutputs  *outputs = '\0';
					if ((outputs = GetOutputs()) == '\0')
					{
						ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: outputs not initialised");
						rc = false;
					}
					else
					{
						TiXmlElement *outputsXml = TinyXmlLib::CreateSection (stepXml, ProcParamsOutputs::xmlOutputs, "function outputs" );
						if (outputsXml == '\0')
						{
							ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: failed to create section for outputsXml");
							rc = false;
						}
						else
						{
							int cnt = outputs->GetParamCount();
							for (int x = 0; x < cnt; x++ )
							{
								ProcParam *param = outputs->GetParam(x);
								if (param == '\0')
								{
									ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: %s failed to get output param %d", _fn->GetFunctionName(), x);
									rc = false;
								}
								else
								{
									TiXmlElement *paramXml = TinyXmlLib::CreateSection (outputsXml, IPluginICProcStepsParamXmlProcParam, "output param" );
									if (paramXml == '\0')
									{
										ImageExptLib::LogMsg("ImageProcStep::SaveSettingsToXml()", "ERROR: failed to create section for out paramXml");
										rc = false;
									}
									else
									{
										paramXml->SetAttribute(IPluginICProcStepsParamAttribName, param->GetName());
										paramXml->SetAttribute(IPluginICProcStepsParamAttribIndex, param->GetIndex());
										paramXml->SetAttribute(IPluginICProcStepsParamAttribType, param->GetType());
										//paramXml->SetAttribute(IPluginICProcStepsParamAttribValue, param->GetValueStr());
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return rc;
}

//-------------------------------------------------------------------------------------------------
//			PRIVATE
//-------------------------------------------------------------------------------------------------


bool ImageProcStep::LoadStep(TiXmlElement *stepXml, ImageProcStepLibs *procLibs)
{
	bool rc = false;

	const char *LibName = '\0';
	if ((LibName = stepXml->Attribute(ImageProcStep::attribLibName)) == '\0')
		ImageExptLib::LogMsg("ImageProcStep::LoadStep", "ERROR: step %d, attrib %s not found", _StepNo, ImageProcStep::attribLibName);
	else
	{
		const char *LibVer = '\0';
		if ((LibVer = stepXml->Attribute(ImageProcStep::attribLibVersion)) == '\0')
			ImageExptLib::LogMsg("ImageProcStep::LoadStep", "ERROR: step %d, attrib %s not found",  _StepNo, ImageProcStep::attribLibVersion);
		else
		{
			int iVer = ImageProcStepLibAuto::BadDllInterface;
			if (stepXml->Attribute(ImageProcStep::attribInterfaceVersion, &iVer) == '\0')
				ImageExptLib::LogMsg("ImageProcStep::LoadStep", "ERROR: step %d, attrib %s not found", _StepNo, ImageProcStep::attribInterfaceVersion);
			else
			{
				IImageProcStepLibDyn *pLib = procLibs->GetLib(LibName, LibVer, iVer);
				if (pLib == '\0')
					ImageExptLib::LogMsg("ImageProcStep::LoadStep", "ERROR: step %d, library %s %s (interface %d) not found",  _StepNo, LibName, LibVer, iVer);
				else
				{
					if (InitialiseStep(stepXml, pLib) == false)
						ImageExptLib::LogMsg("ImageProcStep::LoadStep", "ERROR: step %d, failed to initialise",  _StepNo);
					else
					{
						rc = true;
					}
				}
			}
		}
	}
	return rc;
}

bool ImageProcStep::InitialiseStep(TiXmlElement *stepXml, IImageProcStepLibDyn *DynLib)
{
	bool rc = false;

	const char *FnName = '\0';
	if ((FnName = stepXml->Attribute(ImageProcStep::attribFunctionName)) == '\0')
		ImageExptLib::LogMsg("ImageProcStep::InitialiseStep", "ERROR: step %d, attrib %s not found",  _StepNo, ImageProcStep::attribFunctionName);
	else
	{
		if ( DynLib->IsApiValid() == false)
			ImageExptLib::LogMsg("ImageProcStep::InitialiseStep", "ERROR: step %d, %s %s IsApiValid() failed",  _StepNo, DynLib->GetLibName(), DynLib->GetLibVersion());
		else
		{
			ImageProcStepLibAuto *pLib = DynLib->GetLibApi();
			IImageProcFn *iFn = '\0';
			if ((iFn = pLib->GetFunction(FnName)) == '\0')
				ImageExptLib::LogMsg("ImageProcStep::InitialiseStep", "ERROR: step %d, failed to find function %s in %s %s",  _StepNo, FnName, DynLib->GetLibName(), DynLib->GetLibVersion());
			else
			{
				if (iFn->IsApiValid() == false)
					ImageExptLib::LogMsg("ImageProcStep::InitialiseStep", "ERROR: step %d, function %s->IsApiValid() failed in %s %s",  _StepNo, FnName, DynLib->GetLibName(), DynLib->GetLibVersion());
				else
				{
					_fn = iFn->GetApi();	//guaranteed to work if IsApiValid()

					if ((_inputs = new ProcParamsInputs(stepXml)) == '\0')
						ImageExptLib::LogMsg("ImageProcStep::InitialiseStep", "ERROR: step %d, failed to create _inputs for function %s in %s %s",  _StepNo, FnName, DynLib->GetLibName(), DynLib->GetLibVersion());
					else
					{
						if ((_outputs = new ProcParamsOutputs(stepXml)) == '\0')
						{
							delete _inputs;
							ImageExptLib::LogMsg("ImageProcStep::InitialiseStep", "ERROR: step %d, failed to create _outputs for function %s in %s %s",  _StepNo, FnName, DynLib->GetLibName(), DynLib->GetLibVersion());
						}
						else
						{
							_lib =  DynLib;
							rc = true;
						}
					}
				}
			}
		}
	}
	return rc;
}

void ImageProcStep::SetDescription()
{
	char buff[10];

	if (IsAnalysisStep() == true)
		_Description = "*Step ";
	else
		_Description = " Step ";
	_Description += _itoa(_StepNo, buff, 10);
	_Description += ": ";
	_Description += _lib->GetLibName();
	_Description += " (";		
	_Description += _lib->GetLibVersion();
	_Description += " ) ";
	_Description += _fn->GetFunctionName();
	_Description += "(), ";
	_Description += _fn->GetFunctionDescription();

}

//-------------------------------------------------------------------------------------------------
//			E N D   O F   F I L E
//-------------------------------------------------------------------------------------------------

